nexus\api\rtapi/
group.rs

1use super::RealTimeData;
2use bitfields::bitfield;
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4use std::ffi::{c_char, CStr};
5
6#[derive(Debug, Copy, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct GroupData {
9    /// Locations of squad markers in the game world as ingame coordinates.
10    pub squad_markers: [[f32; 3]; 8],
11
12    /// Type of current group.
13    pub group_type: Result<GroupType, u32>,
14
15    /// Number of members in current group.
16    pub group_member_count: u32,
17}
18
19impl GroupData {
20    /// Reads group data from the given data pointer.
21    ///
22    /// # Safety
23    /// The pointer must be safe to read from.
24    pub unsafe fn read(data: *const RealTimeData) -> Self {
25        Self {
26            squad_markers: (*data).squad_markers,
27            group_type: (*data).group_type.try_into(),
28            group_member_count: (*data).group_member_count,
29        }
30    }
31}
32
33#[derive(
34    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
35)]
36#[num_enum(error_type(name = u32, constructor = From::from))]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38#[cfg_attr(
39    feature = "strum",
40    derive(
41        strum::AsRefStr,
42        strum::Display,
43        strum::EnumCount,
44        strum::EnumIter,
45        strum::IntoStaticStr,
46        strum::VariantArray,
47        strum::VariantNames
48    )
49)]
50#[repr(u32)]
51pub enum GroupType {
52    None,
53    Party,
54    RaidSquad,
55    Squad,
56}
57
58/// Group member.
59///
60/// This struct uses the C layout.
61/// Instead of cloning this it is recommended to convert to [`GroupMemberOwned`] via [`Into`] or [`to_owned`](GroupMember::to_owned).
62#[derive(Debug, Clone)]
63#[repr(C)]
64pub struct GroupMember {
65    /// Account name of the group member.
66    account_name: [c_char; 140],
67
68    /// Character name of the group member.
69    character_name: [c_char; 140],
70
71    /// Subgroup of the group member.
72    ///
73    /// 0 when in a party.
74    /// 1-15 subgrups when in a squad.
75    pub subgroup: u32,
76
77    /// Profession of the group member.
78    ///
79    /// 0 when unknown, for example on loading screen or character select.
80    pub profession: u32,
81
82    /// 3rd specialization of the group member (not always elite).
83    ///
84    /// 0 when unknown, for example on loading screen or character select.
85    pub elite_specialization: u32,
86
87    /// Member flags.
88    flags: GroupMemberFlags,
89}
90
91impl GroupMember {
92    /// Converts the member to a [`GroupMemberOwned`].
93    #[inline]
94    pub fn to_owned(&self) -> GroupMemberOwned {
95        self.into()
96    }
97
98    /// Returns the account name of the member as pointer.
99    #[inline]
100    pub const fn account_name_ptr(&self) -> *const c_char {
101        self.account_name.as_ptr()
102    }
103
104    /// Returns the account name of the member as [`CStr`].
105    #[inline]
106    pub fn account_name_cstr(&self) -> &CStr {
107        unsafe { CStr::from_ptr(self.account_name.as_ptr()) }
108    }
109
110    /// Returns the account name of the member as owned [`String`].
111    #[inline]
112    pub fn account_name(&self) -> String {
113        self.account_name_cstr().to_string_lossy().into_owned()
114    }
115
116    /// Returns the character name of the member as pointer.
117    #[inline]
118    pub const fn character_name_ptr(&self) -> *const c_char {
119        self.character_name.as_ptr()
120    }
121
122    /// Returns the account name of the member as [`CStr`].
123    #[inline]
124    pub fn character_name_cstr(&self) -> &CStr {
125        unsafe { CStr::from_ptr(self.character_name.as_ptr()) }
126    }
127
128    /// Returns the character name of the member as owned [`String`].
129    #[inline]
130    pub fn character_name(&self) -> String {
131        self.character_name_cstr().to_string_lossy().into_owned()
132    }
133
134    /// Returns the flags of the member.
135    #[inline]
136    pub const fn flags(&self) -> GroupMemberFlags {
137        self.flags
138    }
139
140    /// Returns whether the member is self (the local player).
141    #[inline]
142    pub const fn is_self(&self) -> bool {
143        self.flags.is_self()
144    }
145
146    /// Returns whether the member is in the current instance.
147    #[inline]
148    pub const fn is_in_instance(&self) -> bool {
149        self.flags.is_in_instance()
150    }
151
152    /// Returns whether the member if the commander of the current squad.
153    #[inline]
154    pub const fn is_commander(&self) -> bool {
155        self.flags.is_commander()
156    }
157
158    /// Returns whether the member is a lieutenant in the current squad.
159    #[inline]
160    pub const fn is_lieutenant(&self) -> bool {
161        self.flags.is_lieutenant()
162    }
163}
164
165#[bitfield(u32)]
166#[derive(Copy, Clone, PartialEq, Eq)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168pub struct GroupMemberFlags {
169    pub is_self: bool,
170    pub is_in_instance: bool,
171    pub is_commander: bool,
172    pub is_lieutenant: bool,
173
174    #[bits(28)]
175    _padding: u32,
176}
177
178/// Group Member as owned version.
179#[derive(Debug, Clone)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct GroupMemberOwned {
182    /// Account name of the group member.
183    pub account_name: String,
184
185    /// Character name of the group member.
186    pub character_name: String,
187
188    /// 0 for parties, 1-15 according to the squad's subgroup
189    pub subgroup: u32,
190
191    /// 0-9 = Profession; -1 Unknown -> e.g. on loading screen or logged out
192    pub profession: u32,
193
194    /// Third Spec ID, not necessarily elite; or -1 Unknown -> e.g. on loading screen or logged out
195    pub elite_specialization: u32,
196
197    /// Is this member the player themselves?
198    pub is_self: bool,
199
200    /// Is in the same map instance as the player.
201    pub is_in_instance: bool,
202
203    /// Is this member a commander?
204    pub is_commander: bool,
205
206    /// Is this member a lieutenant?
207    pub is_lieutenant: bool,
208}
209
210impl From<&GroupMember> for GroupMemberOwned {
211    fn from(member: &GroupMember) -> Self {
212        Self {
213            account_name: member.account_name(),
214            character_name: member.character_name(),
215            subgroup: member.subgroup,
216            profession: member.profession,
217            elite_specialization: member.elite_specialization,
218            is_self: member.is_self(),
219            is_in_instance: member.is_in_instance(),
220            is_commander: member.is_commander(),
221            is_lieutenant: member.is_lieutenant(),
222        }
223    }
224}